/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/*
 * CHANGES BY BOBW:
 *
 * Adapted for J2ObjC, essentially by adding code for native methods.
 * If you like large chunks of repeated code, you'll definitely have fun here.  :-)
 */

package libcore.io;

import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

/*-[
#import "libcore/io/SizeOf.h"
]-*/

/**
 * Unsafe access to memory.
 */
public final class Memory {
	private static final boolean
		SYSTEM_LITTLE_ENDIAN = isLittleEndian();
	
    private Memory() { }

	private static native boolean isLittleEndian() /*-[
		return CFByteOrderGetCurrent() == CFByteOrderLittleEndian;
	]-*/;

    /**
     * Used to optimize nio heap buffer bulk get operations. 'dst' must be a primitive array.
     * 'dstOffset' is measured in units of 'sizeofElements' bytes.
     */
//    public static native void unsafeBulkGet(Object dst, int dstOffset, int byteCount,
//            byte[] src, int srcOffset, int sizeofElements, boolean swap);

    /**
     * Used to optimize nio heap buffer bulk put operations. 'src' must be a primitive array.
     * 'srcOffset' is measured in units of 'sizeofElements' bytes.
     */
//    public static native void unsafeBulkPut(byte[] dst, int dstOffset, int byteCount,
//            Object src, int srcOffset, int sizeofElements, boolean swap);

    public static int peekInt(byte[] src, int offset, ByteOrder order) {
        if (order == ByteOrder.BIG_ENDIAN) {
            return (((src[offset++] & 0xff) << 24) |
                    ((src[offset++] & 0xff) << 16) |
                    ((src[offset++] & 0xff) <<  8) |
                    ((src[offset  ] & 0xff) <<  0));
        } else {
            return (((src[offset++] & 0xff) <<  0) |
                    ((src[offset++] & 0xff) <<  8) |
                    ((src[offset++] & 0xff) << 16) |
                    ((src[offset  ] & 0xff) << 24));
        }
    }

    public static long peekLong(byte[] src, int offset, ByteOrder order) {
        if (order == ByteOrder.BIG_ENDIAN) {
            int h = ((src[offset++] & 0xff) << 24) |
                    ((src[offset++] & 0xff) << 16) |
                    ((src[offset++] & 0xff) <<  8) |
                    ((src[offset++] & 0xff) <<  0);
            int l = ((src[offset++] & 0xff) << 24) |
                    ((src[offset++] & 0xff) << 16) |
                    ((src[offset++] & 0xff) <<  8) |
                    ((src[offset  ] & 0xff) <<  0);
            return (((long) h) << 32L) | (((long) l) & 0xffffffffL);
        } else {
            int l = ((src[offset++] & 0xff) <<  0) |
                    ((src[offset++] & 0xff) <<  8) |
                    ((src[offset++] & 0xff) << 16) |
                    ((src[offset++] & 0xff) << 24);
            int h = ((src[offset++] & 0xff) <<  0) |
                    ((src[offset++] & 0xff) <<  8) |
                    ((src[offset++] & 0xff) << 16) |
                    ((src[offset  ] & 0xff) << 24);
            return (((long) h) << 32L) | (((long) l) & 0xffffffffL);
        }
    }

    public static short peekShort(byte[] src, int offset, ByteOrder order) {
        if (order == ByteOrder.BIG_ENDIAN) {
            return (short) ((src[offset] << 8) | (src[offset + 1] & 0xff));
        } else {
            return (short) ((src[offset + 1] << 8) | (src[offset] & 0xff));
        }
    }

    public static void pokeInt(byte[] dst, int offset, int value, ByteOrder order) {
        if (order == ByteOrder.BIG_ENDIAN) {
            dst[offset++] = (byte) ((value >> 24) & 0xff);
            dst[offset++] = (byte) ((value >> 16) & 0xff);
            dst[offset++] = (byte) ((value >>  8) & 0xff);
            dst[offset  ] = (byte) ((value >>  0) & 0xff);
        } else {
            dst[offset++] = (byte) ((value >>  0) & 0xff);
            dst[offset++] = (byte) ((value >>  8) & 0xff);
            dst[offset++] = (byte) ((value >> 16) & 0xff);
            dst[offset  ] = (byte) ((value >> 24) & 0xff);
        }
    }

    public static void pokeLong(byte[] dst, int offset, long value, ByteOrder order) {
        if (order == ByteOrder.BIG_ENDIAN) {
            int i = (int) (value >> 32);
            dst[offset++] = (byte) ((i >> 24) & 0xff);
            dst[offset++] = (byte) ((i >> 16) & 0xff);
            dst[offset++] = (byte) ((i >>  8) & 0xff);
            dst[offset++] = (byte) ((i >>  0) & 0xff);
            i = (int) value;
            dst[offset++] = (byte) ((i >> 24) & 0xff);
            dst[offset++] = (byte) ((i >> 16) & 0xff);
            dst[offset++] = (byte) ((i >>  8) & 0xff);
            dst[offset  ] = (byte) ((i >>  0) & 0xff);
        } else {
            int i = (int) value;
            dst[offset++] = (byte) ((i >>  0) & 0xff);
            dst[offset++] = (byte) ((i >>  8) & 0xff);
            dst[offset++] = (byte) ((i >> 16) & 0xff);
            dst[offset++] = (byte) ((i >> 24) & 0xff);
            i = (int) (value >> 32);
            dst[offset++] = (byte) ((i >>  0) & 0xff);
            dst[offset++] = (byte) ((i >>  8) & 0xff);
            dst[offset++] = (byte) ((i >> 16) & 0xff);
            dst[offset  ] = (byte) ((i >> 24) & 0xff);
        }
    }

    public static void pokeShort(byte[] dst, int offset, short value, ByteOrder order) {
        if (order == ByteOrder.BIG_ENDIAN) {
            dst[offset++] = (byte) ((value >> 8) & 0xff);
            dst[offset  ] = (byte) ((value >> 0) & 0xff);
        } else {
            dst[offset++] = (byte) ((value >> 0) & 0xff);
            dst[offset  ] = (byte) ((value >> 8) & 0xff);
        }
    }

    /**
     * Copies 'byteCount' bytes from the source to the destination. The objects are either
     * instances of DirectByteBuffer or byte[]. The offsets in the byte[] case must include
     * the Buffer.arrayOffset if the array came from a Buffer.array call. We could make this
     * private and provide the four type-safe variants, but then ByteBuffer.put(ByteBuffer)
     * would need to work out which to call based on whether the source and destination buffers
     * are direct or not.
     *
     * @hide make type-safe before making public?
     */
//    public static native void memmove(Object dstObject, int dstOffset, Object srcObject, int srcOffset, long byteCount);

	
	
    public static native byte peekByte(long address) /*-[
		return *(char*)address;
	]-*/;

	
	public static native double peekDouble(long address, boolean swap) /*-[
		long long int valueAsLong = [LibcoreIoMemory peekLongWithLongInt:address withBOOL:swap];

		return *(double*)&valueAsLong;
	]-*/;
	
	
    public static native float peekFloat(long address, boolean swap) /*-[
		if (!swap) {
			return *(float*)address;
		}
		else {
			char *pAddress = (char*)address;
			int valueAsInt;
																	  
			if ([LibcoreIoMemory SYSTEM_LITTLE_ENDIAN]) {
				valueAsInt = (((*pAddress++ & 0xff) << 24) |
				((*pAddress++ & 0xff) << 16) |
				((*pAddress++ & 0xff) <<  8) |
				((*pAddress   & 0xff) <<  0));
			}
			else {
				valueAsInt = (((*pAddress++ & 0xff) <<  0) |
				((*pAddress++ & 0xff) <<  8) |
				((*pAddress++ & 0xff) << 16) |
				((*pAddress   & 0xff) << 24));
			}
			
			return *(float*)&valueAsInt;
		}
	]-*/;

	
    public static native int peekInt(long address, boolean swap) /*-[
		if (!swap) {
			return *(int*)address;
		}
		else {
			char *pAddress = (char*)address;
																  
		    if ([LibcoreIoMemory SYSTEM_LITTLE_ENDIAN]) {
			    return (((*pAddress++ & 0xff) << 24) |
			    ((*pAddress++ & 0xff) << 16) |
			    ((*pAddress++ & 0xff) <<  8) |
			    ((*pAddress   & 0xff) <<  0));
		    }
		    else {
			    return (((*pAddress++ & 0xff) <<  0) |
			    ((*pAddress++ & 0xff) <<  8) |
			    ((*pAddress++ & 0xff) << 16) |
			    ((*pAddress   & 0xff) << 24));
		    }
		}
	]-*/;
	
	
    public static native long peekLong(long address, boolean swap) /*-[
		if (!swap) {
			return *(long long int*)address;
		}
		else {
			char *pAddress = (char*)address;
																	
			if ([LibcoreIoMemory SYSTEM_LITTLE_ENDIAN]) {
			    int h =
			    ((*pAddress++ & 0xff) << 24) |
			    ((*pAddress++ & 0xff) << 16) |
			    ((*pAddress++ & 0xff) <<  8) |
			    ( *pAddress++ & 0xff);
			    int l =
			    ((*pAddress++ & 0xff) << 24) |
			    ((*pAddress++ & 0xff) << 16) |
			    ((*pAddress++ & 0xff) <<  8) |
			    ( *pAddress++ & 0xff);
				return (((long long int) h) << 32L) | (((long long int) l) & 0xffffffffL);
            }
			else {
			    int l =
			    ( *pAddress++ & 0xff) |
			    ((*pAddress++ & 0xff) <<  8) |
			    ((*pAddress++ & 0xff) << 16) |
			    ((*pAddress++ & 0xff) << 24);
			    int h =
			    ( *pAddress++ & 0xff) |
			    ((*pAddress++ & 0xff) <<  8) |
			    ((*pAddress++ & 0xff) << 16) |
			    ((*pAddress++ & 0xff) << 24);
				return (((long long int) h) << 32L) | (((long long int) l) & 0xffffffffL);
            }
		}
	]-*/;
	
	
    public static native short peekShort(long address, boolean swap) /*-[
		if (!swap) {
			return *(short int*)address;
		}
		else {
			char *pAddress = (char*)address;
																	  
			if ([LibcoreIoMemory SYSTEM_LITTLE_ENDIAN]) {
			    return (short int) ((*pAddress << 8) | (pAddress[1] & 0xff));
			}
			else {
				return (short int) ((pAddress[1] << 8) | (*pAddress & 0xff));
            }
		}
	]-*/;

	
	
    public static native void peekByteArray(long address, byte[] dst, int dstOffset, int byteCount) /*-[
		[dst replaceBytes:(char*)address offset:dstOffset length:byteCount];
	]-*/;
	
	
    public static native void peekCharArray(long address, char[] dst, int dstOffset, int count, boolean swap) /*-[
		if (!swap) {
			[dst replaceChars:(unichar *)address offset:dstOffset length:count];
		}
		else {
			unichar *pDst;
			
			[dst getUnsafeBufferAddress:(char**)&pDst];
			pDst += dstOffset;
			
			unichar *pDstLimit = pDst + count;
			
			while (pDst < pDstLimit) {
				*pDst++ = [LibcoreIoMemory peekShortWithLongInt:address withBOOL:swap];
				address += LibcoreIoSizeOf_CHAR;
			}
		}
	]-*/;
	
	
    public static native void peekDoubleArray(long address, double[] dst, int dstOffset, int count, boolean swap) /*-[
		if (!swap) {
			[dst replaceDoubles:(double *)address offset:dstOffset length:count];
		}
		else {
			double *pDst;
			
			[dst getUnsafeBufferAddress:(char**)&pDst];
			pDst += dstOffset;
			
			double *pDstLimit = pDst + count;
			
			while (pDst < pDstLimit) {
				*pDst++ = [LibcoreIoMemory peekDoubleWithLongInt:address withBOOL:swap];
				address += LibcoreIoSizeOf_DOUBLE;
			}
		}
	]-*/;
	
	
    public static native void peekFloatArray(long address, float[] dst, int dstOffset, int count, boolean swap) /*-[
		if (!swap) {
			[dst replaceFloats:(float *)address offset:dstOffset length:count];
		}
		else {
			float *pDst;
			
			[dst getUnsafeBufferAddress:(char**)&pDst];
			pDst += dstOffset;
			
			float *pDstLimit = pDst + count;
			
			while (pDst < pDstLimit) {
				*pDst++ = [LibcoreIoMemory peekFloatWithLongInt:address withBOOL:swap];
				address += LibcoreIoSizeOf_FLOAT;
			}
		}
	]-*/;
	
	
    public static native void peekIntArray(long address, int[] dst, int dstOffset, int count, boolean swap) /*-[
		if (!swap) {
			[dst replaceInts:(int *)address offset:dstOffset length:count];
		}
		else {
			int *pDst;
			
			[dst getUnsafeBufferAddress:(char**)&pDst];
			pDst += dstOffset;
			
			int *pDstLimit = pDst + count;
			
			while (pDst < pDstLimit) {
				*pDst++ = [LibcoreIoMemory peekIntWithLongInt:address withBOOL:swap];
				address += LibcoreIoSizeOf_INT;
			}
		}
	]-*/;
	
	
    public static native void peekLongArray(long address, long[] dst, int dstOffset, int count, boolean swap) /*-[
		if (!swap) {
			[dst replaceLongs:(long long int *)address offset:dstOffset length:count];
		}
		else {
			long long int *pDst;
			
			[dst getUnsafeBufferAddress:(char**)&pDst];
			pDst += dstOffset;
			
			long long int *pDstLimit = pDst + count;
			
			while (pDst < pDstLimit) {
				*pDst++ = [LibcoreIoMemory peekLongWithLongInt:address withBOOL:swap];
				address += LibcoreIoSizeOf_LONG;
			}
		}
	]-*/;
	
	
    public static native void peekShortArray(long address, short[] dst, int dstOffset, int count, boolean swap) /*-[
		if (!swap) {
			[dst replaceShorts:(short *)address offset:dstOffset length:count];
		}
		else {
			short int *pDst;
			
			[dst getUnsafeBufferAddress:(char**)&pDst];
			pDst += dstOffset;
			
			short int *pDstLimit = pDst + count;
			
			while (pDst < pDstLimit) {
				*pDst++ = [LibcoreIoMemory peekShortWithLongInt:address withBOOL:swap];
				address += LibcoreIoSizeOf_SHORT;
			}
		}
	]-*/;

	
	
    public static native void pokeByte(long address, byte value) /*-[
		*(char*)address = value;
	]-*/;

	
	public static native void pokeDouble(long address, double value, boolean swap) /*-[
		[LibcoreIoMemory pokeLongWithLongInt:address withLongInt:*(long*)&value withBOOL:swap];
	]-*/;

	
	public static native void pokeFloat(long address, float value, boolean swap) /*-[
		if (!swap) {
			*(float*)address = value;
		}
		else {
			char *pAddress = (char*)address;
			int valueAsInt = *(int*)&value;
			
			if ([LibcoreIoMemory SYSTEM_LITTLE_ENDIAN]) {
				*pAddress++ = (char) ((valueAsInt >> 24) & 0xff);
				*pAddress++ = (char) ((valueAsInt >> 16) & 0xff);
				*pAddress++ = (char) ((valueAsInt >>  8) & 0xff);
				*pAddress   = (char) ( valueAsInt        & 0xff);
			}
			else {
				*pAddress++ = (char) ( valueAsInt        & 0xff);
				*pAddress++ = (char) ((valueAsInt >>  8) & 0xff);
				*pAddress++ = (char) ((valueAsInt >> 16) & 0xff);
				*pAddress   = (char) ((valueAsInt >> 24) & 0xff);
			}
		}
	]-*/;

	
    public static native void pokeInt(long address, int value, boolean swap) /*-[
		if (!swap) {
			*(int*)address = value;
		}
		else {
			char *pAddress = (char*)address;
																			  
			if ([LibcoreIoMemory SYSTEM_LITTLE_ENDIAN]) {
			    *pAddress++ = (char) ((value >> 24) & 0xff);
			    *pAddress++ = (char) ((value >> 16) & 0xff);
			    *pAddress++ = (char) ((value >>  8) & 0xff);
			    *pAddress   = (char) ( value        & 0xff);
            }
			else {
			    *pAddress++ = (char) ( value        & 0xff);
			    *pAddress++ = (char) ((value >>  8) & 0xff);
			    *pAddress++ = (char) ((value >> 16) & 0xff);
			    *pAddress   = (char) ((value >> 24) & 0xff);
            }
		}
	]-*/;
	
	
    public static native void pokeLong(long address, long value, boolean swap) /*-[
		if (!swap) {
			*(long long int*)address = value;
		}
		else {
			char *pAddress = (char*)address;
																				
		    if ([LibcoreIoMemory SYSTEM_LITTLE_ENDIAN]) {
			    int i = (int) (value >> 32);
			    *pAddress++ = (char) ((i >> 24) & 0xff);
			    *pAddress++ = (char) ((i >> 16) & 0xff);
				*pAddress++ = (char) ((i >>  8) & 0xff);
				*pAddress++ = (char) ( i        & 0xff);
				i = (int) value;
				*pAddress++ = (char) ((i >> 24) & 0xff);
				*pAddress++ = (char) ((i >> 16) & 0xff);
				*pAddress++ = (char) ((i >>  8) & 0xff);
				*pAddress   = (char) ( i        & 0xff);
		    }
			else {
				int i = (int) value;
				*pAddress++ = (char) ( i        & 0xff);
				*pAddress++ = (char) ((i >>  8) & 0xff);
				*pAddress++ = (char) ((i >> 16) & 0xff);
				*pAddress++ = (char) ((i >> 24) & 0xff);
				i = (int) (value >> 32);
				*pAddress++ = (char) ( i        & 0xff);
				*pAddress++ = (char) ((i >>  8) & 0xff);
				*pAddress++ = (char) ((i >> 16) & 0xff);
				*pAddress   = (char) ((i >> 24) & 0xff);
            }
		}
	]-*/;
	
	
    public static native void pokeShort(long address, short value, boolean swap) /*-[
		if (!swap) {
			*(short int*)address = value;
		}
		else {
			char *pAddress = (char*)address;
																				  
			if ([LibcoreIoMemory SYSTEM_LITTLE_ENDIAN]) {
			    *pAddress++ = (char) ((value >> 8) & 0xff);
			    *pAddress   = (char) ( value       & 0xff);
            }
			else {
			    *pAddress++ = (char) ( value       & 0xff);
			    *pAddress   = (char) ((value >> 8) & 0xff);
            }
		}
	]-*/;

	
	
    public static native void pokeByteArray(long address, byte[] src, int srcOffset, int count) /*-[
		[src getBytes:(char*)address offset:srcOffset length:count];
	]-*/;
	
	
    public static native void pokeCharArray(long address, char[] src, int srcOffset, int count, boolean swap) /*-[
		if (!swap) {
			[src getChars:(unichar *)address offset:srcOffset length:count];
		}
		else {
			unichar *pSrc;
																											
			[src getUnsafeBufferAddress:(char**)&pSrc];
			pSrc += srcOffset;
																											
			unichar *pSrcLimit = pSrc + count;
																											
			while (pSrc < pSrcLimit) {
				[LibcoreIoMemory pokeShortWithLongInt:address withShortInt:(short int)*pSrc++ withBOOL:swap];
				address += LibcoreIoSizeOf_CHAR;
			}
		}
	]-*/;
	
	
    public static native void pokeDoubleArray(long address, double[] src, int srcOffset, int count, boolean swap) /*-[
		if (!swap) {
			[src getDoubles:(double *)address offset:srcOffset length:count];
		}
		else {
			double *pSrc;
																												
			[src getUnsafeBufferAddress:(char**)&pSrc];
			pSrc += srcOffset;
																												
			double *pSrcLimit = pSrc + count;
																												
			while (pSrc < pSrcLimit) {
				[LibcoreIoMemory pokeDoubleWithLongInt:address withDouble:*pSrc++ withBOOL:swap];
				address += LibcoreIoSizeOf_DOUBLE;
			}
		}
	]-*/;
	
	
    public static native void pokeFloatArray(long address, float[] src, int srcOffset, int count, boolean swap) /*-[
		if (!swap) {
			[src getFloats:(float *)address offset:srcOffset length:count];
		}
		else {
			float *pSrc;
																											  
			[src getUnsafeBufferAddress:(char**)&pSrc];
			pSrc += srcOffset;
																											  
			float *pSrcLimit = pSrc + count;
																											  
			while (pSrc < pSrcLimit) {
				[LibcoreIoMemory pokeFloatWithLongInt:address withFloat:*pSrc++ withBOOL:swap];
				address += LibcoreIoSizeOf_FLOAT;
			}
		}
	]-*/;
	
	
    public static native void pokeIntArray(long address, int[] src, int srcOffset, int count, boolean swap) /*-[
		if (!swap) {
			[src getInts:(int *)address offset:srcOffset length:count];
		}
		else {
			int *pSrc;
																										  
			[src getUnsafeBufferAddress:(char**)&pSrc];
			pSrc += srcOffset;
																										  
			int *pSrcLimit = pSrc + count;
																										  
			while (pSrc < pSrcLimit) {
				[LibcoreIoMemory pokeIntWithLongInt:address withInt:*pSrc++ withBOOL:swap];
				address += LibcoreIoSizeOf_INT;
			}
		}
	]-*/;
	
	
    public static native void pokeLongArray(long address, long[] src, int srcOffset, int count, boolean swap) /*-[
		if (!swap) {
			[src getLongs:(long long int *)address offset:srcOffset length:count];
		}
		else {
			long long int *pSrc;
																											
			[src getUnsafeBufferAddress:(char**)&pSrc];
			pSrc += srcOffset;
																											
			long long int *pSrcLimit = pSrc + count;
																											
			while (pSrc < pSrcLimit) {
				[LibcoreIoMemory pokeLongWithLongInt:address withLongInt:*pSrc++ withBOOL:swap];
			address += LibcoreIoSizeOf_LONG;
		}
	}
	]-*/;
	
	
    public static native void pokeShortArray(long address, short[] src, int srcOffset, int count, boolean swap) /*-[
		if (!swap) {
			[src getShorts:(short int *)address offset:srcOffset length:count];
		}
		else {
			short int *pSrc;
																											  
			[src getUnsafeBufferAddress:(char**)&pSrc];
			pSrc += srcOffset;
																											  
			short int *pSrcLimit = pSrc + count;
																											  
			while (pSrc < pSrcLimit) {
				[LibcoreIoMemory pokeShortWithLongInt:address withShortInt:*pSrc++ withBOOL:swap];
				address += LibcoreIoSizeOf_SHORT;
			}
		}
	]-*/;
}
